package org.trc.traffic.analysis;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

public class AggMLinkCongestionClustering {
	double timeCost = 0;

	//输入参数
	protected float minSpatialThreshold;

	protected float minTimeThreshold;
	
	protected int minNum;
	
	//聚簇结果
	protected Map<String, AggMLinkCongestionCluster> congestionClusterSet;

	//聚簇
	public void buildAggCongestionCluster(String mLinkID, ArrayList <MLinkCongestionCluster> allMLinkClusterList, float minSpatialThreshold, float minTimeThreshold, int minNum) {

		timeCost = System.currentTimeMillis();
		
        // 数据集初始化
		this.minSpatialThreshold = minSpatialThreshold;

		this.minTimeThreshold = minTimeThreshold;
		
		this.minNum = minNum;

		// LinkCongestionCluster分组

		int clusterNum = 1;
		MLinkCongestionCluster mlinkCC = null;

		for (int i = 0; i < allMLinkClusterList.size(); i++) {
			mlinkCC =  allMLinkClusterList.get(i);
			if (!mlinkCC.clusterID.startsWith("AggMLink")) {// 未分组
				if(this.expandAggCongestionCluster(mlinkCC, allMLinkClusterList,mLinkID, clusterNum))
					clusterNum++;
			}
		}

		// 生成 AggLinkCongestionCluster
		this.congestionClusterSet = Collections.synchronizedMap(new HashMap<String, AggMLinkCongestionCluster>());
		
		AggMLinkCongestionCluster congestionCluster = null;

		for (MLinkCongestionCluster mlCC : allMLinkClusterList) {
			String clusterID = mlCC.clusterID;
			if(!clusterID.startsWith("AggMLink"))
				continue;
			if (!congestionClusterSet.containsKey(clusterID)) {
				congestionCluster = new AggMLinkCongestionCluster(clusterID);
				
				System.out.println(clusterID);
				
				congestionCluster.add(mlCC);
				congestionClusterSet.put(clusterID, congestionCluster);
			} else{
				congestionCluster = congestionClusterSet.get(clusterID);
			    congestionCluster.add(mlCC);
			}
		}
		
		timeCost = System.currentTimeMillis() - this.timeCost;

	}

	private boolean expandAggCongestionCluster(MLinkCongestionCluster coreMLinkCluster, ArrayList<MLinkCongestionCluster> allMLinkClusterList,
			String mLinkID, int clusterNum) {	

		List<MLinkCongestionCluster> resultDataSet = this.STRangeQuery(coreMLinkCluster, allMLinkClusterList);

		if (resultDataSet.size() <  minNum)
			return false;

		System.out.println("Num:" + resultDataSet.size());
		
		coreMLinkCluster.clusterID = "AggMLink_" + mLinkID + "_" + Integer.toString(clusterNum);
		
		for (MLinkCongestionCluster mlCC : resultDataSet) {// 标记簇ID
			if (mlCC.clusterID.startsWith("Mlink")){
				mlCC.clusterID = "AggMLink_" + mLinkID + "_" + Integer.toString(clusterNum);
				expandAggCongestionCluster(mlCC, allMLinkClusterList, mLinkID, clusterNum);			
			}
		}
		
		return true;
	}

	//时空邻域
	private List<MLinkCongestionCluster> STRangeQuery(MLinkCongestionCluster coreMLinkCluster, ArrayList<MLinkCongestionCluster> allMLinkClusterList) {
		Vector<MLinkCongestionCluster> stRange_List = new Vector<MLinkCongestionCluster>();

		for (MLinkCongestionCluster mlCC : allMLinkClusterList) {
			if (mlCC.equals(coreMLinkCluster))
				continue;
			if (timeWithin(mlCC, coreMLinkCluster)&& spatialWithin(mlCC, coreMLinkCluster))
				stRange_List.add(mlCC);
		}

		return stRange_List;
	}

	//空间相似Jarcard计算
	private boolean spatialWithin(MLinkCongestionCluster mlCC1, MLinkCongestionCluster mlCC2) {
		float union =  Math.max(mlCC1.endOffset, mlCC2.endOffset) - Math.min(mlCC1.startOffset, mlCC2.startOffset);
		
		float intersection =  Math.min(mlCC1.endOffset, mlCC2.endOffset) - Math.max(mlCC1.startOffset, mlCC2.startOffset);
		
		if(intersection <= 0)
			return false;
		
		float spatialSimilarity = intersection/union;
		
		if(spatialSimilarity > this.minSpatialThreshold)
			return true;
		else
			return false;	
	}

	//时间相似Jarcard计算
	private boolean timeWithin(MLinkCongestionCluster mlCC1, MLinkCongestionCluster mlCC2) {

		float union =  Math.max(mlCC1.endTime, mlCC2.endTime) - Math.min(mlCC1.startTime, mlCC2.startTime) + 1;
		
		float intersection =  Math.min(mlCC1.endTime, mlCC2.endTime) - Math.max(mlCC1.startTime, mlCC2.startTime) + 1;
		
		if(intersection <= 0)
			return false;
		
		float timeSimilarity = intersection/union;
		
		if(timeSimilarity > this.minTimeThreshold)
			return true;
		else
			return false;	
	}

	public ArrayList <AggCongestionCluster> getCongestionCluster() {
		return new ArrayList <AggCongestionCluster> (congestionClusterSet.values());
	}
	
	public static void main(String[] args) throws ParseException{
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
		
		ArrayList <MLinkCongestionCluster> allMLinkClusterList = new ArrayList <MLinkCongestionCluster> ();
		
		STMLinkCongestionClustering stMLinkCongestionClustering = new STMLinkCongestionClustering();
		
		stMLinkCongestionClustering.buildCongestionCluster("31612", 7, 8, dateFormat.parse("2009-3-8"), 300, 2);
			
		allMLinkClusterList.addAll((Collection<? extends MLinkCongestionCluster>) stMLinkCongestionClustering.getCongestionCluster());
		
						
		stMLinkCongestionClustering.buildCongestionCluster("31612", 7, 8, dateFormat.parse("2009-3-9"), 300, 2);
		
		allMLinkClusterList.addAll((Collection<? extends MLinkCongestionCluster>) stMLinkCongestionClustering.getCongestionCluster());
		
		
		stMLinkCongestionClustering.buildCongestionCluster("31612", 7, 8, dateFormat.parse("2009-3-10"), 300, 2);
		
		allMLinkClusterList.addAll((Collection<? extends MLinkCongestionCluster>) stMLinkCongestionClustering.getCongestionCluster());
		
		
		stMLinkCongestionClustering.buildCongestionCluster("31612", 7, 8, dateFormat.parse("2009-3-11"), 300, 2);
		
		allMLinkClusterList.addAll((Collection<? extends MLinkCongestionCluster>) stMLinkCongestionClustering.getCongestionCluster());
		
		
		stMLinkCongestionClustering.buildCongestionCluster("31612", 7, 8, dateFormat.parse("2009-3-12"), 300, 2);
		
		allMLinkClusterList.addAll((Collection<? extends MLinkCongestionCluster>) stMLinkCongestionClustering.getCongestionCluster());
		
		
		AggMLinkCongestionClustering aggMLinkCongestionClustering = new AggMLinkCongestionClustering();
		
		aggMLinkCongestionClustering.buildAggCongestionCluster("31612", allMLinkClusterList, 0.3f, 0.3f, 2);
		
		ArrayList<AggCongestionCluster> congestionClusters = aggMLinkCongestionClustering.getCongestionCluster();
		
		for (AggCongestionCluster congestionCluster : congestionClusters) {
			AggMLinkCongestionCluster cc = (AggMLinkCongestionCluster)congestionCluster;
			System.out.print("CluterID: " + cc.clusterID + ",");
			System.out.print(" StartTime: " + (cc.startTime) + ",");
			System.out.print(" EndtTime: " + (cc.endTime));
			System.out.print(" StartOffset: " + cc.startOffset + ",");
			System.out.print(" EndtOffset: " + cc.endOffset);
			System.out.print(" Num: " + cc.num);
			System.out.println();
		}
	
	}

}
